/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.common.dataset.support.poi;

import com.google.common.collect.Lists;
import org.apache.poi.hssf.eventusermodel.*;
import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingRowDummyRecord;
import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.vacoor.nothing.common.dataset.RecordConsumer;
import org.vacoor.nothing.common.dataset.support.SimpleRecord;
import org.vacoor.nothing.common.util.Throwables;

import java.io.InputStream;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
 * POI 的流式 API
 *
 * @author vacoor
 */
public class PoiReader implements HSSFListener {
    private static final int PROCESS_ALL = -1;

    private FormatTrackingHSSFListener formatListener;
    private SSTRecord sstRecord;
    private List<BoundSheetRecord> boundSheetRecords;

    private int sheetIndex;
    private String sheetName;

    private int maxColumnSize = 0;
    private int stringFormulaColumn;

    private final RecordConsumer consumer;
    private int processSheet = PROCESS_ALL;
    private List<Object> rowData;

    public static PoiReader use(RecordConsumer consumer) {
        return new PoiReader(consumer);
    }

    private PoiReader(RecordConsumer consumer) {
        this.consumer = consumer;
    }

    public void read(InputStream in) {
        read(in, PROCESS_ALL);
    }

    public void read(InputStream in, int processSheetIndex) {
        try {
            POIFSFileSystem fs = new POIFSFileSystem(in);

            boundSheetRecords = new LinkedList<BoundSheetRecord>();
            processSheet = processSheetIndex;

            // 处理缺失 Record, 包装 Record, 方便处理
            MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);

            // 处理 format
            formatListener = new FormatTrackingHSSFListener(listener);
            HSSFEventFactory factory = new HSSFEventFactory();

            HSSFRequest request = new HSSFRequest();
            request.addListenerForAllRecords(formatListener);
            factory.processWorkbookEvents(request, fs);
        } catch (Exception e) {
            throw Throwables.unchecked(e);
        } finally {
            processSheetIndex = PROCESS_ALL;
        }
    }

    @Override
    public void processRecord(Record record) {
        if (record instanceof BoundSheetRecord) {
            // sheet 边界信息
            boundSheetRecords.add((BoundSheetRecord) record);
        } else if (record instanceof BOFRecord && BOFRecord.TYPE_WORKSHEET == ((BOFRecord) record).getType()) {
            sheetName = boundSheetRecords.get(sheetIndex++).getSheetname();
        } else if (record instanceof SSTRecord) {
            // Share String Table Record
            sstRecord = (SSTRecord) record;

            //} else if (record instanceof RowRecord) {

            //----
        }

        // 不处理所有, 并且当前不是给定的 sheet
        if (PROCESS_ALL != processSheet && sheetIndex - 1 != processSheet) {
            return;
        }

        //----

        if (record instanceof RowRecord) {
            // 行信息, 没必要了, 每次拿到的总是最大列数
            // maxColumnSize = Math.max(rowRecord.getLastCol(), maxColumnSize);
            maxColumnSize = ((RowRecord) record).getLastCol();
            if (rowData == null) {
                rowData = Lists.newLinkedList();
            }
        } else if (record instanceof MissingRowDummyRecord) {
            // 处理缺失的行, 这个由MissingRecordAwareHSSFListener包装

        } else if (record instanceof MissingCellDummyRecord) {
            // 缺失值的cell
            rowData.add(((MissingCellDummyRecord) record).getColumn(), null);
        } else if (record instanceof LastCellOfRowDummyRecord) {
            // 行的最后一个元素
            LastCellOfRowDummyRecord lastCellOfRowDummyRecord = (LastCellOfRowDummyRecord) record;
            final int row = lastCellOfRowDummyRecord.getRow();
            final int lastColumn = lastCellOfRowDummyRecord.getLastColumnNumber();// 如果最后一列列号为－1, 则该行缺失

            // 填充不存在的单元格到最大宽度
            for (int i = lastColumn + 1; i < maxColumnSize; i++) {
                rowData.add(i, null);
            }

            // 跳过空行
            if (lastColumn > -1) {
                consumer.onRecord(new SimpleRecord(row, Collections.unmodifiableList(rowData)));
                rowData = Lists.newLinkedList();
            }
        } else if (record instanceof CellValueRecordInterface) {    // cell
            CellValueRecordInterface valueRecord = (CellValueRecordInterface) record;
            int col = valueRecord.getColumn();

            if (valueRecord instanceof BoolErrRecord) {
                // 布尔类型
                rowData.add(col, ((BoolErrRecord) valueRecord).getBooleanValue());
            } else if (valueRecord instanceof NumberRecord) {
                // 数值类型
                NumberRecord numberic = (NumberRecord) valueRecord;
                double value = numberic.getValue();

                // 日期类型
                if (isDateRecord(formatListener, numberic)) {
                    rowData.add(col, HSSFDateUtil.getJavaDate(value));
                } else {
                    rowData.add(col, value);
                }

            } else if (valueRecord instanceof LabelRecord) {
                rowData.add(col, ((LabelRecord) valueRecord).getValue());
            } else if (valueRecord instanceof LabelSSTRecord) { // 引用共享字符串表的 label 类型
                LabelSSTRecord labelSST = (LabelSSTRecord) valueRecord;
                String string = sstRecord.getString(labelSST.getSSTIndex()).getString();
                rowData.add(col, string);
            } else if (valueRecord instanceof FormulaRecord) {
                FormulaRecord formula = (FormulaRecord) valueRecord;
                int resultType = formula.getCachedResultType();

                // 如果公式的值是一个字符串, 则结果存在下一个 record
                if (HSSFCell.CELL_TYPE_BLANK == resultType || HSSFCell.CELL_TYPE_ERROR == resultType) {
                    rowData.add(col, null);
                } else if (HSSFCell.CELL_TYPE_BOOLEAN == resultType) {
                    int value = Double.valueOf(formula.getValue()).intValue();
                    rowData.add(col, value != 0);
                } else if (HSSFCell.CELL_TYPE_STRING == resultType) {
                    stringFormulaColumn = col;
                } else {    // 默认为 Number
                    double value = formula.getValue();

                    // 日期类型
                    if (isDateRecord(formatListener, formula)) {
                        rowData.add(col, HSSFDateUtil.getJavaDate(value));
                    } else {
                        rowData.add(col, value);
                    }
                }
            }
        } else if (record instanceof StringRecord) {    // 公式的字符串值
            rowData.add(stringFormulaColumn, ((StringRecord) record).getString());
            stringFormulaColumn = -1;
        }
    }

    /**
     * 是否是日期 record
     *
     * @param formatListener
     * @param record
     * @return
     */
    private boolean isDateRecord(FormatTrackingHSSFListener formatListener, CellValueRecordInterface record) {
        int formatIndex = formatListener.getFormatIndex(record);
        String formatString = formatListener.getFormatString(record);
        return HSSFDateUtil.isADateFormat(formatIndex, formatString);
    }
}
